﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using FunctionalCLR.Core.Function;

namespace FunctionalCLR.Core {
    public sealed class Utils {

        public static string LogFile {
            get {
                return ConfigurationManager.AppSettings["log"];
            }
        }

        public static int Precision {
            get {
                string p 
                    = ConfigurationManager.AppSettings["precision"];
                if (string.IsNullOrEmpty(p)) {
                    return 5;
                }

                return Convert.ToInt32(p);
            }
            set {
                ConfigurationManager.AppSettings["precision"] = Convert.ToString(value);
            }
        }

        public static string ModelPath {
            get {
                string path 
                    = ConfigurationManager.AppSettings["models"];
                //if (string.IsNullOrEmpty(path)) {
                //    throw new ArgumentException(
                //        "Model path is not configured.");
                //}

                return path;
            }
            set {
                if (!Directory.Exists(value)) {
                    throw new DirectoryNotFoundException(value);
                }

                ConfigurationManager.AppSettings["models"] = value;
            }
        }

        public static string ConstantFile {
            get {
                string path 
                    = ConfigurationManager.AppSettings["constant"];
                //if (string.IsNullOrEmpty(path)) {
                //    throw new ArgumentException(
                //        "Constant file is not configured.");
                //}

                return path;
            }
        }

        public static string FunctionPath {
            get {
                string path 
                    = ConfigurationManager.AppSettings["functions"];
                //if (string.IsNullOrEmpty(path)) {
                //    throw new ArgumentException(
                //        "Model path is not configured.");
                //}

                return path;
            }
            set {
                string[] s = value.Split(';');
                foreach(string loc in s) {
                    if(!Directory.Exists(loc)) {
                        throw new DirectoryNotFoundException(loc);
                    }
                }

                ConfigurationManager.AppSettings["functions"] = value;
            }
        }

        public static object GetObject(string assemblyName, string typeName) {
            Assembly assembly = Assembly.LoadFrom(assemblyName);
            object instance = (object)assembly.CreateInstance(typeName);

            return instance;
        }

        public static string ReplaceString(
            string from, string to, string ex) {
            return ReplaceString(
                from, to, ex, ReplacingMode.CompleteMatch);
        }

        public static string ReplaceString(
            string from, string to, string ex, ReplacingMode mode) {
            if (string.IsNullOrEmpty(ex)) {
                return string.Empty;
            }

            StringBuilder buffer = new StringBuilder();

            Lexer lexer = new Lexer(ex);
            while (lexer.HasMoreTokens()) {
                Token token = lexer.NextToken();
                if (token.TokenType == TokenType.Identifier) {
                    switch (mode) {
                        case ReplacingMode.CompleteMatch:
                            if (token.Value == from) {
                                token.Value = to;
                            }
                            break;
                        case ReplacingMode.StartingMatch:
                            if (token.Value.StartsWith(from)) {
                                string tail = token.Value.Substring(
                                    from.Length,
                                    token.Value.Length - from.Length - 1);
                                token.Value = from + tail;
                            }
                            break;
                        case ReplacingMode.EndingMatch:
                            if (token.Value.EndsWith(from)) {
                                string tail = token.Value.Substring(
                                    0,
                                    token.Value.Length - from.Length - 1);
                                token.Value = from + tail;
                            }
                            break;
                        case ReplacingMode.AnyMatch:
                            break;
                    }
                }

                buffer.Append(token.Value);
            }

            return buffer.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expr"></param>
        public static string CheckExpression(string expr) {
            switch (expr) {
                case "ln": return "Math.Log";
                case "exp": return "Math.Exp";
                case "sqrt": return "Math.Sqrt";
                case "sin": return "Math.Sin";
                case "cos": return "Math.Cos";
                case "pi": return "Math.PI";
                case "asin": return "Math.Asin";
                case "acos": return "Math.Acos";
                case "atan": return "Math.Atan";
                case "tan": return "Math.Acos";
                case "tanh": return "Math.Atan";
                case "sinh": return "Math.Sinh";
                case "cosh": return "Math.Cosh";
                default: break;
            }

            return expr;
        }

        public static string PrefixizeFunction(
            string prefix, string expr, IFunctionManager funMgr) {
            return Prefixize(prefix, expr, false, funMgr);
        }

        public static string Prefixize(
            string prefix, string expr, IFunctionManager funMgr) {
            return Prefixize(prefix, expr, true, funMgr);
        }

        public static string Prefixize(
            string prefix, string ex,
            bool tag, IFunctionManager funMgr) {
            if (string.IsNullOrEmpty(ex)) {
                return string.Empty;
            }

            StringBuilder buffer = new StringBuilder();

            Lexer lexer = new Lexer(ex);
            while (lexer.HasMoreTokens()) {
                Token token = lexer.NextToken();
                if (token.TokenType == TokenType.Identifier) {
                    char nc = lexer.PeekChar();
                    if (tag) {
                        if (nc != '(' && !DefaultDependencyManager.Instance.ConstantManager.IsConstant(token.Value)) {
                            token.Value = string.IsNullOrEmpty(prefix)
                                ? token.Value : prefix + "." + token.Value;
                        }
                    } else {
                        if (nc == '(' &&
                            token.Value.IndexOf('.') < 0
                            && funMgr.IsUserDefinedFunction(token.Value)) {
                            token.Value = string.IsNullOrEmpty(prefix)
                                ? token.Value : prefix + "." + token.Value;
                        }
                    }
                }
                buffer.Append(token.Value);
            }

            return buffer.ToString();
        }

        public static bool IsNumeric(object s) {
            double result = 0;
            bool r = Double.TryParse(s.ToString(), out result);

            return r;
        }

        public static bool IsDebugging {
            get {
                object val = ConfigurationManager.AppSettings["debugging"];
                if (val == null) {
                    return false;
                }

                return Convert.ToBoolean(val);
            }
        }

        public static void Debugging(string p, params string[] p_2) {
            if (IsDebugging) {
                Console.WriteLine(p, p_2);
            }
        }

        public static void Log(string p, params string[] p_2) {
            if (!EventLog.SourceExists("Zove Desktop")) {
                EventLog.CreateEventSource("Zove Desktop", "Zove Desktop");
            }

            EventLog.WriteEntry("Zove Desktop", string.Format(p, p_2));
        }

        public static void Debugging(Exception ex) {
            if (IsDebugging) {
                Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
            }
        }

        public static List<string> GetAllIdentifiers(string ex) {
            List<string> words = new List<string>();

            Lexer lexer = new Lexer(ex);
            while (lexer.HasMoreTokens()) {
                Token token = lexer.NextToken();
                if (token.TokenType == TokenType.Identifier) {
                    char nc = lexer.PeekChar();
                    if (nc != '(' && !DefaultDependencyManager.Instance.ConstantManager.IsConstant(token.Value)) {
                        words.Add(token.Value);
                    }
                }
            }

            return words;
        }

        public static string InitUDF(
            UserDefinedFunctionNode node, string[] args) {
            List<string> argList = new List<string>(args);
            argList.Reverse();
            args = argList.ToArray();

            UserDefinedFunctionNode udf
                = node as UserDefinedFunctionNode;
            string expr = udf.FuncBody;

            for (int index = udf.Parameters.Length - 1;
                index >= 0; index--) {
                expr = Utils.ReplaceString(
                    udf.Parameters[index],
                    args[index], expr,
                    ReplacingMode.CompleteMatch
                    );
            }

            return expr;
        }
    }
}
